package cn.tedu.mall.seckill.timer.job;

import cn.tedu.mall.pojo.seckill.model.SeckillSku;
import cn.tedu.mall.pojo.seckill.model.SeckillSpu;
import cn.tedu.mall.seckill.mapper.SeckillSkuMapper;
import cn.tedu.mall.seckill.mapper.SeckillSpuMapper;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.RandomUtils;
import org.bouncycastle.pqc.math.linearalgebra.RandUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;


@Slf4j
@Component
public class SeckillInitialJob implements Job {

    //查询spu相关信息的mapper
    @Autowired
    private SeckillSpuMapper seckillSpuMapper;
    //查询sku相关信息的mapper
    @Autowired
    private SeckillSkuMapper seckillSkuMapper;
    //操作Radis的对象
    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // 当前方法的目标是将sku库存数和随机码预热到redis中
        // 本方法运行时机是秒杀开始前5分钟,所以我们需要创建一个5分钟的时间,用于查询秒杀的商品列表
        LocalDateTime  time = LocalDateTime.now().plusMinutes(5);
        //查询5分钟后所有进行秒杀的spu商品列表
        List<SeckillSpu> seckillSpus = seckillSpuMapper.findSeckillSpusByTime(time);
        //遍历当前批次所有参加秒杀的spu列表
        for (SeckillSpu spu : seckillSpus){
            //我们要将sku的库存预热到redis，所有要根据当前spuId查询sku列表
            List<SeckillSku> seckillSkus = seckillSkuMapper.findSeckillSkuBySpuId(spu.getSpuId());
            //遍历seckillSkus集合,从中获取sku对象,以向Redis中保存它的库存数
            for (SeckillSku sku : seckillSkus){
                log.info("开始将{}号的sku库存数预热到redis",sku.getSkuId());
                // 要操作Redis,需要先确定Redis的Key,一般都是定义好的常量
                // SeckillCacheUtils.getStockKey是返回字符串常量的方法
                // 这个方法的参数sku.getSkuId(),会追加到字符串常量的最后
                // skuStockKey最后的值可能是:mall:seckill:sku:stock:1
                String skuStockKey = SeckillCacheUtils.getStockKey(sku.getSkuId());
                //检查Redis中是否已经存在这个key
                if (redisTemplate.hasKey(skuStockKey)){
                    //如果已经存在，证明已经缓存过了，直接跳过
                    log.info("{}号sku的库存数，已经缓存过了",sku.getSkuId());
                }else {
                    //如果这个key不存在,就要将库存数保存到Redis中,保存时间：秒杀时间+提前预热存到Redis的时间+防雪崩随机数30秒【1000*60*60*2+1000*60*5+30s】
                    redisTemplate.boundValueOps(skuStockKey).set(sku.getSeckillStock(),1000*60*60*2+1000*60*5+ RandomUtils.nextInt(30000), TimeUnit.MICROSECONDS);
                    log.info("{}号sku的库存数成功预热到缓存!",sku.getSkuId());
                }
            }
            //上面是内层循环结束，遍历的是spu对象，我们生成的随机码和spu关联
            String randCodeKey = SeckillCacheUtils.getRandCodeKey(spu.getSpuId());
            //判断当前key是否在radis中
            if (redisTemplate.hasKey(randCodeKey)){
                //如果随机码存在，不需要任何操作，为了测试，输出在控制台
                int randCode = (int) redisTemplate.boundValueOps(randCodeKey).get();
                log.info("{}号sou商品的随机码已经缓存过了，值为：{}",spu.getSpuId(),randCode);
            }else {
                //如果key不存在，就需要生成随机码保存在Redis，设计六位数随机码
                int randCode = RandomUtils.nextInt(900000) + 100000;
                redisTemplate.boundValueOps(randCodeKey).set(randCode,1000*60*5+ RandomUtils.nextInt(10000),TimeUnit.MILLISECONDS);

            }
        }
    }
}
